// minna-switch-box.ino

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>
#include <ArduinoJson.h>
#include <PubSubClient.h>
#include <OneButton.h>

#define FIRMWARE_VERSION "1.0.0"
#define CONFIG_FILE "/config.json"
#define DEBUG_PRINT true

#if DEBUG_PRINT
#define DEBUG(...) Serial.printf(__VA_ARGS__)
#else
#define DEBUG(...)
#endif

const int pinBtn = 12;
int pinLight = 13;
OneButton button(pinBtn, true);
unsigned long longPressStartTime = 0;

const int pinSW = 4;
const int pinSW1 = 5;
int State = 0;

WiFiClient wClient;
PubSubClient mqttClient(wClient);
ESP8266WebServer server(80);
bool isInSoftAP = false;

char wifi_ssid[32] = "";
char wifi_password[32] = "";
char deviceID[20] = "test";
char mqtt_server[40] = "192.168.0.104";
char mqtt_port[6] = "1883";
char mqtt_user[20] = "minna";
char mqtt_password[20] = "1234567890";

unsigned long lastReconnect = 0;
const unsigned long reconnectInterval = 4000;

String topicReport_DeviceInfo, topicReport_StatusRep, topicStatus_req, topicSet;

void saveConfig() {
  DEBUG("[Minna] Saving configuration...");
  StaticJsonDocument<512> doc;
  doc["wifi_ssid"] = wifi_ssid;
  doc["wifi_password"] = wifi_password;
  doc["deviceID"] = deviceID;
  doc["mqtt_server"] = mqtt_server;
  File file = SPIFFS.open(CONFIG_FILE, "w");
  if (file) serializeJson(doc, file);
  file.close();
  DEBUG("[Minna] Config saved.\n");
}

void loadConfig() {
  if (!SPIFFS.exists(CONFIG_FILE)) return;
  File file = SPIFFS.open(CONFIG_FILE, "r");
  if (!file) return;
  StaticJsonDocument<512> doc;
  if (!deserializeJson(doc, file)) {
    strlcpy(wifi_ssid, doc["wifi_ssid"], sizeof(wifi_ssid));
    strlcpy(wifi_password, doc["wifi_password"], sizeof(wifi_password));
    if (doc.containsKey("deviceID")) strlcpy(deviceID, doc["deviceID"], sizeof(deviceID));
    if (doc.containsKey("mqtt_server")) strlcpy(mqtt_server, doc["mqtt_server"], sizeof(mqtt_server));
  }
  file.close();
  DEBUG("[Minna] Config loaded.\n");
}

void startConfigPortal() {
  DEBUG("[Minna] Starting SoftAP mode for configuration.\n");
  WiFi.mode(WIFI_AP);
  WiFi.softAP("minna-setup");
  isInSoftAP = true;
  server.on("/", HTTP_GET, []() {
    char page[378];
    snprintf(page, sizeof(page),
        "<form action='/save' method='POST'>WiFi:<input name='wifi_ssid' value='%s'> PW:<input name='wifi_password' value='%s'><br>DeviceID:<input name='deviceID' value='%s'><br>MQTT Server:<input name='mqtt_server' value='%s'><br><input type='submit' value='OK'></form>",
        wifi_ssid, wifi_password, deviceID, mqtt_server);
    server.send(200, "text/html", page);
  });
  server.on("/save", HTTP_POST, []() {
    strlcpy(wifi_ssid, server.arg("wifi_ssid").c_str(), sizeof(wifi_ssid));
    strlcpy(wifi_password, server.arg("wifi_password").c_str(), sizeof(wifi_password));
    strlcpy(deviceID, server.arg("deviceID").c_str(), sizeof(deviceID));
    strlcpy(mqtt_server, server.arg("mqtt_server").c_str(), sizeof(mqtt_server));
    saveConfig();
    server.send(200, "text/html", "Save OK. Restarting...");
    delay(1000);
    ESP.restart();
  });
  server.begin();
  DEBUG("[Minna] Web server started in SoftAP mode. Connect to 192.168.4.1\n");
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  char message[256] = {0};
  size_t copyLen = (length < sizeof(message) - 1) ? length : sizeof(message) - 1;
  memcpy(message, payload, copyLen);
  StaticJsonDocument<200> doc;
  if (deserializeJson(doc, message)) return;
  if (String(topic) == topicSet) controlSwitch(doc["state"] == 1);
  else if (String(topic) == topicStatus_req) publishStatus(State);
}

void publishDeviceInfo() {
  String msg = "{\"type\":\"switch\", \"id\":\"" + String(deviceID) + "\"}";
  mqttClient.publish(topicReport_DeviceInfo.c_str(), msg.c_str());
}

void publishStatus(int state) {
  StaticJsonDocument<200> doc;
  doc["state"] = state;
  doc["deviceid"] = deviceID;
  char jsonBuffer[512];
  serializeJson(doc, jsonBuffer);
  mqttClient.publish(topicReport_StatusRep.c_str(), jsonBuffer);
}

void controlSwitch(bool state) {
  digitalWrite(pinSW, state ? LOW : HIGH);
  delay(200);
  digitalWrite(pinSW1, state ? HIGH : LOW);
  State = state;
  publishStatus(State);
}

void mqttReconnect() {
  if (!mqttClient.connected()) {
    DEBUG("[Minna] Attempting MQTT reconnect...\n");
    if (mqttClient.connect(deviceID, mqtt_user, mqtt_password)) {
      mqttClient.subscribe(topicSet.c_str());
      mqttClient.subscribe(topicStatus_req.c_str());
      publishDeviceInfo();

      DEBUG("[Minna] MQTT connected.\n");
    } else {
      DEBUG("[Minna] MQTT connect failed: %d\n", mqttClient.state());

      static unsigned long lastBlink = 0;
      static bool ledState = false;
      if (millis() - lastBlink >= 500) {
          lastBlink = millis();
          ledState = !ledState;
          digitalWrite(pinLight, ledState ? HIGH : LOW);
      }
    }
  }
  else
  {
    digitalWrite(pinLight, LOW);
  }
}

void setup() {
  Serial.begin(115200);
  DEBUG("[Minna] Booting Minna ESP8266 SoftAP WiFi+MQTT firmware v%s\n", FIRMWARE_VERSION);
  DEBUG("[Minna] Initializing SPIFFS...\n");
  SPIFFS.begin();
  DEBUG("[Minna] Loading configuration...\n");
  loadConfig();

  pinMode(pinSW, OUTPUT);
  pinMode(pinSW1, OUTPUT);
  pinMode(pinBtn, INPUT_PULLUP);

  pinMode(pinLight, OUTPUT);
  digitalWrite(pinLight, LOW);
  delay(500);
  digitalWrite(pinLight, HIGH);
  delay(500);
  digitalWrite(pinLight, LOW);

  button.attachClick([](){
    int v = digitalRead(pinSW);
    if (HIGH==v)
    {
      controlSwitch(true);
    }
    else
    {
      controlSwitch(false);
    }
  });
  button.attachLongPressStart([]() {
    longPressStartTime = millis();
  });
  button.attachLongPressStop([]() {
    unsigned long diff = millis() - longPressStartTime;
    if (diff > 6000) {
      digitalWrite(pinLight, LOW);
      delay(1000);
      digitalWrite(pinLight, HIGH);
      delay(1000);
      digitalWrite(pinLight, LOW);

      DEBUG("[Minna] Button long press 6000: entering SoftAP config mode.\n");
      memset(wifi_ssid, 0, sizeof(wifi_ssid));
      memset(wifi_password, 0, sizeof(wifi_password));
      saveConfig(); // Save cleared configuration
      startConfigPortal();
    }
    else if (diff > 2000)
    {
      DEBUG("[Minna] Button long press 2000: esp restart.\n");

      digitalWrite(pinLight, LOW);
      delay(200);
      digitalWrite(pinLight, HIGH);
      delay(200);
      digitalWrite(pinLight, LOW);

      Serial.println("esp restart"); 
      ESP.restart();
    }
  });

  DEBUG("[Minna] Setting WiFi to STA mode...\n");
  WiFi.mode(WIFI_STA);
  if (strlen(wifi_ssid) > 0) {
    DEBUG("[Minna] Attempting to connect to WiFi SSID: %s\n", wifi_ssid);
    WiFi.begin(wifi_ssid, wifi_password);
    if (WiFi.waitForConnectResult() != WL_CONNECTED) {
      DEBUG("[Minna] WiFi connection failed. Entering SoftAP mode.\n");
      startConfigPortal();
    } else {
      DEBUG("[Minna] WiFi connected. IP address: %s\n", WiFi.localIP().toString().c_str());
    }
  } else {
    startConfigPortal();
  }

  topicReport_DeviceInfo = "home/device/info";
  topicReport_StatusRep = "home/device/status_rep/" + String(deviceID);
  topicStatus_req = "home/device/status_req/" + String(deviceID);
  topicSet = "home/device/set/" + String(deviceID);

  mqttClient.setServer(mqtt_server, atoi(mqtt_port));
  DEBUG("[Minna] Connecting to MQTT server %s:%s...\n", mqtt_server, mqtt_port);
  mqttClient.setCallback(mqttCallback);
}

void loop() {
  button.tick();
  if (isInSoftAP) {
    server.handleClient();
    return;
  }
  mqttClient.loop();
  if (millis() - lastReconnect > reconnectInterval) {
    lastReconnect = millis();
    mqttReconnect();
  }
}
